package com.kuwou.workobject.Utils;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.kuwou.workobject.pojo.entity.Channel;
import com.kuwou.workobject.pojo.entity.Comment;
import com.kuwou.workobject.pojo.entity.Music;
import com.kuwou.workobject.pojo.entity.RankList;
import com.kuwou.workobject.service.ChannelService;
import com.kuwou.workobject.service.Impl.MusicServiceImpl;
import com.kuwou.workobject.service.RankListService;
import lombok.RequiredArgsConstructor;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

@RequiredArgsConstructor
@Component
public class UrlUtil {

    private final @Lazy RankListService rankListService;
    private final @Lazy ChannelService channelService;
    private final @Lazy MusicServiceImpl musicService;
    private static final String CHROME_DRIVER_PATH = "C:\\Program Files\\Google\\Chrome\\Application\\chromedriver.exe"; // 修改为你的chromedriver路径

    /**
     * 从指定的URL爬取音乐榜单数据
     * 该方法使用Selenium WebDriver来驱动浏览器获取网页内容，并使用Jsoup解析HTML
     * 它遍历多个音乐榜单，提取每个榜单中的音乐信息，并将这些信息存储在一个Map中
     *
     * @param url 网页地址，用于爬取音乐榜单数据
     * @return 返回一个Map，其中键是榜单名称，值是该榜单中的音乐列表
     */
    public static Map<String,Map<String,List<Music>>> getData(String url) {

        Map<String,Map<String,List<Music>>> channelRankMusics = new HashMap<>();

        // 获取Logger
        Logger logger = LoggerFactory.getLogger(UrlUtil.class);

        // 1. 配置 ChromeOptions
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--start-maximized"); // 启动时最大化窗口（覆盖任务栏）

        System.setProperty("webdriver.chrome.driver", CHROME_DRIVER_PATH);
        WebDriver driver = new ChromeDriver(options);

        logger.info("开始....打开浏览器...");

        int rankCount = 0;
        int musicCount = 0;

        try {
            driver.get(url);
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

            System.out.println("开始爬取数据...");
            logger.info("开始爬取数据...");

            // 新增：获取所有频道标签
            List<WebElement> channels = driver.findElements(By.cssSelector(".tabs.flex_c > span"));

            // 遍历每个频道
            for (int channelIndex = 0; channelIndex < channels.size(); channelIndex++) {

                Map<String,List<Music>> Musics = new HashMap<>();
                // 获取频道名称
                WebElement channel = channels.get(channelIndex);

                // 切换频道（如果不是当前激活频道）
                if (!channel.getAttribute("class").contains("active")) {
                    channel.click();
                    // 等待频道内容加载（可根据实际页面结构调整等待条件）
                    wait.until(ExpectedConditions.presenceOfElementLocated(
                            By.cssSelector("ul.tab_con > li.flex_c")
                    ));
                    System.out.println("\n========== 切换至 [" + channel.getText() + "] 频道 ==========");
                    logger.info("\n========== 切换至 [" + channel.getText() + "] 频道 ==========");
                }

                // 获取当前频道下的所有榜单
                List<WebElement> rankItems = driver.findElements(By.cssSelector("ul.tab_con > li.flex_c"));

                System.out.println("当前频道 [" + channel.getText() + "] 包含 " + rankItems.size() + " 个榜单");
                logger.info("当前频道 [" + channel.getText() + "] 包含 " + rankItems.size() + " 个榜单");

                rankCount += rankItems.size();

//                Thread.sleep(300);
                // 遍历当前频道的所有榜单
                for (int i = 0; i < rankItems.size(); i++) {

                    // 生成当前榜单的XPath
                    String liSize = i+1+"";
                    String xpath = "/html/body/div[1]/div/div/div[2]/div/div[2]/div[1]/div/ul/li["+liSize+"]/div/p[1]";
                    // 点击切换榜单（使用XPath精确定位）
                    WebElement targetRank = wait.until(ExpectedConditions.elementToBeClickable(
                            By.xpath(
                                    xpath
                            )));

                    // 使用 JavaScript 滚动到元素位置
                    ((JavascriptExecutor) driver).executeScript(
                            "arguments[0].scrollIntoView(true);",
                            targetRank
                    );

                    // 等待元素可见后再点击
                    wait.until(ExpectedConditions.visibilityOf(targetRank));

                    targetRank.click();

                    Thread.sleep(1000); // 简单等待页面稳定

                    // 获取当前榜单名称
                    String currentRankName = driver.findElement(
                            By.cssSelector("li.flex_c.active .item_info .name")
                    ).getText();

                    logger.info("\n========== 切换至 [" + currentRankName + "] 榜单 ==========");


                    String pageSource = driver.getPageSource();
                    Document document = Jsoup.parse(pageSource);

                    Elements pageCount = document.select("div.con_r > div > div.page-wrap > ul.flex_c > li");

                    int pageCountSize = Integer.parseInt(pageCount.get(pageCount.size() - 1).text());


                    // 获取当前榜单的分页数量
//                    List<WebElement> pageCount = driver.findElements(By.cssSelector("div.con_r > div > div.page-wrap > ul.flex_c > li"));
//                    List<WebElement> pageCount = driver.findElements(By.xpath("/html/body/div/div/div/div[2]/div/div[2]/div[2]/div[1]/div[4]/ul"));
//                    pageCount.forEach(li -> {
//                        System.out.println("当前分页数据[" + li.getText() + "]");
//                    });
//
//                    int pageCountSize = 1;

                    System.out.println("当前榜单 [" + currentRankName + "] 包含 " + pageCountSize + " 页");
                    logger.info("当前榜单 [" + currentRankName + "] 包含 " + pageCountSize + " 页");


                    // 爬取当前榜单数据
                    List<Music> musicList = new ArrayList<>();
                    int musicItem = 0;
                    int currentRankMusicCount = 0;
                    for (int page = 1; page <= pageCountSize; page++) {
                        Document doc = Jsoup.parse(driver.getPageSource());
                        musicList.addAll(parseAndObject(doc));

                        List<WebElement> rankList = driver.findElements(By.cssSelector("ul.rank_list > li"));

                        logger.info("当前页 [" + page + "] 包含 " + rankList.size() + " 首歌");

                        musicItem = rankList.size();

                        currentRankMusicCount += musicItem;


                        // 分页处理（保持原有逻辑）
                            if (page < pageCountSize) {
                                WebElement nextPage = wait.until(ExpectedConditions.elementToBeClickable(
                                        By.cssSelector(".page-wrap .li-page.iconfont.icon-icon_pagedown")
                                ));
                                nextPage.click();
                                Thread.sleep(500);
                            }
                    }


                    // 更新总歌数
                    musicCount += currentRankMusicCount;

                    System.out.println("\n--- "+ currentRankName + " 爬取成功，共 " + currentRankMusicCount + " 首歌 ---");
                    logger.info("--- "+ currentRankName + " 爬取成功，共 " + currentRankMusicCount + " 首歌 ---");
                    Musics.put(currentRankName, musicList);
                    channelRankMusics.put(channel.getText(), Musics);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            driver.quit();
        }

        System.out.println("总共爬取了" + channelRankMusics.size() + "个频道");
        System.out.println("共爬取 " + rankCount + " 个榜单数据");
        System.out.println("共爬取 " + musicCount + " 首歌数据");

        logger.info("总共爬取了" + channelRankMusics.size() + "个频道");
        logger.info("共爬取 " + rankCount + " 个榜单数据");
        logger.info("共爬取 " + musicCount + " 首歌数据");
        logger.info("数据处理完成！");
        return channelRankMusics;
    }

    public static List<Comment> getComment(List<RankList> rankLists){

        // 1. 配置 ChromeOptions
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--start-maximized"); // 启动时最大化窗口（覆盖任务栏）

        System.setProperty("webdriver.chrome.driver", CHROME_DRIVER_PATH);
        WebDriver driver = new ChromeDriver(options);
        List<Comment> comments = new ArrayList<>();
        try {
            driver.get("https://kuwo.cn/rankList");
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

            System.out.println("开始爬取数据...");

            // 新增：获取所有频道标签
            List<WebElement> channels = driver.findElements(By.cssSelector(".tabs.flex_c > span"));

            // 遍历每个频道
            for (int channelIndex = 0; channelIndex < channels.size(); channelIndex++) {

                // 获取频道名称
                WebElement channel = channels.get(channelIndex);

                // 切换频道（如果不是当前激活频道）
                if (!channel.getAttribute("class").contains("active")) {
                    channel.click();
                    // 等待频道内容加载（可根据实际页面结构调整等待条件）
                    wait.until(ExpectedConditions.presenceOfElementLocated(
                            By.cssSelector("ul.tab_con > li.flex_c")
                    ));
                    System.out.println("\n========== 切换至 [" + channel.getText() + "] 频道 ==========");
                }

                // 获取当前频道下的所有榜单
                List<WebElement> rankItems = driver.findElements(By.cssSelector("ul.tab_con > li.flex_c"));

                // 遍历当前频道的所有榜单
                for (int i = 0; i < rankItems.size(); i++) {

                    // 生成当前榜单的XPath
                    String liSize = i+1+"";
                    String xpath = "/html/body/div[1]/div/div/div[2]/div/div[2]/div[1]/div/ul/li["+liSize+"]/div/p[1]";
                    // 点击切换榜单（使用XPath精确定位）
                    WebElement targetRank = wait.until(ExpectedConditions.elementToBeClickable(
                            By.xpath(
                                    xpath
                            )));

                    // 使用 JavaScript 滚动到元素位置
                    ((JavascriptExecutor) driver).executeScript(
                            "arguments[0].scrollIntoView(true);",
                            targetRank
                    );

                    // 等待元素可见后再点击
                    wait.until(ExpectedConditions.visibilityOf(targetRank));

                    targetRank.click();

                    Thread.sleep(1000); // 简单等待页面稳定

                    // 获取当前榜单名称
                    String currentRankName = driver.findElement(
                            By.cssSelector("li.flex_c.active .item_info .name")
                    ).getText();

                    int currentRanListId = 0;

                    for (RankList rankList : rankLists) {
                        if (rankList.getName().equals(currentRankName)) {
                            currentRanListId = rankList.getId();
                            break;
                        }
                    }

                    List<WebElement> hotCommentList = driver.findElements(By.cssSelector("section.all-comment > div.each-comment"));
                    List<WebElement> newCommentList = driver.findElements(By.cssSelector("section.all-comment > div.new-comment-out > div"));


                    for (WebElement webElement : hotCommentList) {
                        String text = webElement.getText();
                        String[] split = text.split("\n");
                        ArrayList<String> list = new ArrayList<>();
                        if (split.length < 3){
                            list.add(split[0]);
                            list.add("未知信息");
                            list.add(split[1]);
                        }else{
                            list.add(split[0]);
                            list.add(split[1]);
                            list.add(split[2]);
                        }
                        if(split.length > 3){
                            if (list.get(1).contains("回复") && list.get(1).contains("@") || list.get(2).contains("@")){
                                String temp = list.get(1) + "被回复人的内容：" +list.get(2);
                                list.set(1,temp);
                                list.set(2,split[3]);
                            }
                            if(!list.get(1).contains("回复") && !list.get(1).contains("@")) {
                                String temp = list.get(1)+list.get(2);
                                list.set(1,temp);
                                list.set(2,split[3]);
                            }
                        }
                        comments.add(new Comment(list.get(0), list.get(1), list.get(2),currentRanListId,"hot"));
                    }

                    for (WebElement webElement : newCommentList) {
                        String text = webElement.getText();
                        String[] split = text.split("\n");
                        ArrayList<String> list = new ArrayList<>();
                        if (split.length < 3){
                            list.add(split[0]);
                            list.add("未知信息");
                            list.add(split[1]);
                        }else{
                            list.add(split[0]);
                            list.add(split[1]);
                            list.add(split[2]);
                        }
                        if(split.length > 3){
                            if (list.get(1).contains("回复") && list.get(1).contains("@")){
                                String temp = list.get(1) + "被回复人的内容：" +list.get(2);
                                list.set(1,temp);
                                list.set(2,split[3]);
                            }
                        }
                        comments.add(new Comment(list.get(0), list.get(1), list.get(2),currentRanListId,"new"));
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            driver.quit();
        }
        return comments;
    }


    //解析数据
    private static List<Music> parseAndObject(Document doc) throws IOException {
        List<Music> musics = new ArrayList<>();
        Elements songs = doc.select("li.song_item.flex_c");
        int num = 0;

        for (Element song : songs) {
            num++;
            // 解析各个字段
            String rank = song.selectFirst(".song_rank.flex_c > div").text();
            String title = song.selectFirst(".song_name > a").text();
            String artist = song.selectFirst(".song_artist > span").text();
            String time = song.selectFirst(".song_time > span").text();

            if ("".equals(rank)){
                rank = num + "";
            }
            Music music = new Music(rank, title, artist, time);
            musics.add(music);
        }
        return musics;
    }

    //获取频道列表
    public static List<Channel> getChannelList() throws IOException {
        Connection connect = Jsoup.connect("https://kuwo.cn/rankList");
        Document document = connect.get();
        Elements channelName = document.select("div.tabs.flex_c > span");
        List<String> channelNames = new ArrayList<>();
        for (Element element : channelName) {
            channelNames.add(element.text());
        }

        List<Channel> channels = new ArrayList<>();

        for (String name : channelNames) {
            channels.add(new Channel(name));
        }

        for (Channel channel : channels) {
            System.out.println(channel);
        }
        return channels;
    }

    //获取数据
    public String getData() throws IOException {

        //清空频道表
        channelService.truncateTable();
        // 保存频道
        channelService.saveBatch(UrlUtil.getChannelList());

        // 调用UrlUtil.getData方法获取音乐数据
        //Map<频道名称,Map<榜单名称,List<歌曲实体>>>
        Map<String, Map<String, List<Music>>> data = UrlUtil.getData("https://kuwo.cn/rankList");

        // 获取数据条目迭代器
        Iterator<Map.Entry<String, Map<String, List<Music>>>> iterator = data.entrySet().iterator();
        // 清空榜单表
        rankListService.truncateTable();

        //清空歌曲表
        musicService.truncateTable();

        //歌曲列表
        List<Music> musicList = new ArrayList<>();

        // 遍历数据条目
        while (iterator.hasNext()) {

            // 获取当前条目
            Map.Entry<String, Map<String, List<Music>>> entry = iterator.next();

            //频道名称
            String channelName = entry.getKey();

            //构建获取频道ID条件器
            LambdaQueryWrapper<Channel> queryWrapper =
                    new LambdaQueryWrapper<>(Channel.class)
                            .eq(Channel::getName, channelName);

            //获取频道实体
            Channel channel = Db.getOne(queryWrapper); // 正确：添加实体类参数

            // 频道ID
            int channelId = channel.getId();

            // 获取当前频道的榜单
            Map<String, List<Music>> rankList = entry.getValue();

            // 创建一个新的map，只包含当前榜单，跟榜单下面的歌曲
            Map<RankList,List<Music>> rank = new HashMap<>();

            // 遍历当前频道的榜单
            Iterator<Map.Entry<String, List<Music>>> rankIterator = rankList.entrySet().iterator();
            System.out.println(channelName + "频道总共爬取了" + rankList.size() + "个榜单");
            while (rankIterator.hasNext()) {
                //获取榜单条目
                Map.Entry<String, List<Music>> ranks = rankIterator.next();

                String rankName = ranks.getKey();
                musicList = ranks.getValue();

                rank.put(new RankList(rankName, channelId, musicList.size(), LocalDateTime.now()),musicList);

            }

            List<RankList> rankLists = new ArrayList<>();


            rank.entrySet().iterator().forEachRemaining(ent -> {
                rankLists.add(ent.getKey());
            });

            // 保存榜单
            rankListService.saveBatch(rankLists);
            List<Music> musicLists = new ArrayList<>();
            Iterator<Map.Entry<RankList, List<Music>>> iteratored = rank.entrySet().iterator();
            while (iteratored.hasNext()) {
                Map.Entry<RankList, List<Music>> e = iteratored.next();
                RankList rankListTemp = e.getKey();
                int rankId = rankListTemp.getId();
                int rankChannelId = rankListTemp.getChannelId();
                List<Music> temp = e.getValue();

                temp.forEach(music -> {
                    music.setId(0);
                    music.setRankListId(rankId);
                    music.setChannelId(rankChannelId);
                });
                musicLists.addAll(temp);
            }
            musicService.saveBatch(musicLists,musicLists.size());
        }
        return "true";
    }



    //获取歌曲链接
    public static Map<String,String> getMusicLink(String musicName) throws IOException {
//        String musicData = "匿名的朋友";
        String type = "music";
        Connection connect = Jsoup.connect("http://www.2t58.com/so/"+musicName+"/1.html")
//                .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0")
//                .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
//                .header("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2")
//                .header("Referer", "https://www.2t58.com/")
                .timeout(10000);
        Document document = connect.get();

        Elements select = document.select(".play_list > ul > li > div.name > a");
        String link = "";
        // 检查是否存在元素
        if (!select.isEmpty()) {
            // 获取第一个匹配的<a>标签
            Element element = select.first();

            // 提取href属性值
            link = element.attr("abs:href"); // 使用abs:获取绝对路径
            System.out.println("歌曲链接: " + link);
        } else {
            System.out.println("未找到匹配的链接");
        }

        String[] split = link.split("\\/");
        String idParam  =  split[split.length-1].replace(".html","");
        Map<String,String> result = new HashMap<>();
        result.put("idParam",idParam);
        result.put("type",type);
        return result;
    }

    //下载歌曲
    public static String downloadMusic(String musicName ,String audioUrl) throws IOException {
        File file = new File("D:\\java\\WorkObject\\src\\main\\resources\\music");
        int num = file.list().length;
        String result = "";
        String savePath = "D:\\java\\WorkObject\\src\\main\\resources\\music\\"+num+".aac"; // 保存的文件名
        try {
            downloadAudio(audioUrl, savePath);
            String absolutePath = new File(savePath).getAbsolutePath();
            System.out.println("音频下载成功，保存路径: " + absolutePath);

            result = num + ".aac";

        } catch (Exception e) {
            System.err.println("下载失败: " + e.getMessage());
            result = "false";
            e.printStackTrace();
        }

        return result;
    }

    public static void downloadAudio(String audioUrl, String savePath) throws IOException {
        URL url = new URL(audioUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");

        int responseCode = connection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            Path filePath = Paths.get(savePath);

            // 修复点：检查父目录是否存在
            if (filePath.getParent() != null) {
                Files.createDirectories(filePath.getParent()); // 仅在需要时创建目录
            }

            try (InputStream in = connection.getInputStream();
                 OutputStream out = Files.newOutputStream(filePath,
                         StandardOpenOption.CREATE,
                         StandardOpenOption.TRUNCATE_EXISTING)) {

                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
                out.flush();
            }
        } else {
            throw new IOException("服务器返回错误码: " + responseCode);
        }
        connection.disconnect();
    }

}
